
package node;

import java.io.Serializable;
import java.rmi.RemoteException;
import java.util.HashMap;
import java.util.Map;

import channel.VirtualNodeChannel;

/**
 * This is a virtual node class that extends AbstractNode.
 * 
 * @author TCSS 558 Team 6
 * @version Spring 2013
 * 
 */
public class VirtualNode
{
  /**
   * This is the parent actual node.
   */
  private ActualNode my_parent;

  /**
   * The mapping between the connection channels between this node and its
   * neighbors.
   */
  private final Map<Integer, VirtualNodeChannel> my_vchannels =
      new HashMap<Integer, VirtualNodeChannel>();;

  /**
   * It is the identified ID for one node.
   */
  private final int my_id;

  /**
   * 2^ my_M_value is the total number of all the nodes.
   */
  protected final int my_m_value;
  /**
   * This is the set of Serializable data that stores on each node.
   */
  private final Map<String, Serializable> my_data = new HashMap<String, Serializable>();

  /**
   * 
   * We may need a field in here that keeps data for queries replies that came
   * back after an actual node died and became virtual.
   * 
   * That way the next time this node comes to life it will get the response it
   * had requested before it went down.
   * 
   * Fault tolerance beyond scope at this time.
   * 
   */

  /**
   * Constructor.
   * 
   * @param the_id is the Id of this virtual node.
   * @param the_parent is the actual parent node.
   */
  public VirtualNode(final int the_id, final ActualNode the_parent, final int the_m)
  {
    my_id = the_id;
    my_m_value = the_m;
    my_parent = the_parent;
    try
    {
      initializeVChannel(the_m);
    }
    catch (final RemoteException e)
    {
      e.printStackTrace();
    }
  }

  /**
   * Constructor, we may need to create a node that has some giving data.
   * 
   * @param the_id is the Id of this virtual node.
   * @param the_parent is the actual parent node.
   * @param the_data is the giving data.
   */
  public VirtualNode(final int the_id, final ActualNode the_parent,
                     final Map<String, Serializable> the_data, final int the_m)
  {
    this(the_id, the_parent, the_m);
    my_data.putAll(the_data);
  }

  /**
   * When a new actual node added, some virtual nodes will reassign to other
   * actual node. So it will change its parent node.
   * 
   * @param the_parent is the new parent node.
   */
  public void relocateParent(final ActualNode the_parent)
  {
    my_parent = the_parent;
  }

  public ActualNode getmyActiveNode()
  {
    return my_parent;
  }

  /**
   * Gets the data according to the key value.
   * 
   * @param the_key is the key.
   * @return a serializable object.
   * @throws RemoteException  if there is a problem completing the method call.
   */
  public Serializable get(final String the_key)
  {
    return my_data.get(the_key);
  }

  /**
   * Puts the data on one node.
   * 
   * @param the_key is the key.
   * @param the_data is the map value.
   * @return false if the key is existed we don't overwrite it, otherwise true
   *         and put the key-value in the map.
   */
  public boolean put(final String the_key, final Serializable the_data)
  {

    final boolean exist = my_data.containsKey(the_key);
    if (!exist)
    {
      my_data.put(the_key, the_data);
    }
    return !exist;
  }

  /**
   * @return the my_id
   */
  public int getMyID()
  {
    return my_id;
  }

  /**
   * 
   * Initializes all the channels.
   * 
   * @param my_actual_list is the collection of all actual 'nodes.
   * @throws RemoteException if there is a problem completing the method call.
   */
  public void initializeVChannel(final int the_m) throws RemoteException
  {
    // initializes the channel
    for (int i = 0; i != the_m; ++i)
    {
      final VirtualNodeChannel channel = new VirtualNodeChannel(my_id, i, the_m);
      my_vchannels.put(i, channel);
    }
  }

  /**
   * Probes the channels and gives a node id that covers this node.
   * 
   * @param the_node is the node that need to probe its next hop.
   * @return return the id of its next hop.
   */
  public int probeChannels(final int the_node)
  {
    int result = my_id;
 
    for (int i = 0; i != my_vchannels.size(); ++i)
    {
      
      if (my_vchannels.get(i).isInInterval(the_node))
      {
        result = my_vchannels.get(i).getChannelId();
        System.out.println("");
        break;
      }
    }
    return result;
  }
/**
 * Gets the data. 
 * @return data
 */
  public Map<String, Serializable> getData()
  {
    return my_data;
  }

}
